// i.r(t), i.d(t, { default: () => Fatcaster })
import Module from "./Module"
import SymbolList from "../SymbolList"
import RaycasterModule from "./RaycasterModule"
import * as THREE from "three"
import Vectors from "../three/Vectors"
import Filter from "../three/Filter"
import EngineContext from "../EngineContext"
import WebGLRendererModule from "./WebGLRendererModule"

export default class Fatcaster extends Module {
  rayAssembly: THREE.Ray[]
  static rayCount: number
  rayPlaneOrientation: THREE.Quaternion
  rayVisuals: THREE.ArrowHelper[]
  config: { debug: boolean }
  raycaster: RaycasterModule
  constructor() {
    super(...arguments)
    this.name = "fat-caster"
    this.rayAssembly = new Array(Fatcaster.rayCount)
    this.rayPlaneOrientation = new THREE.Quaternion()
    this.rayVisuals = []
  }
  async init(e, t: EngineContext) {
    this.config = { debug: !!e.debug }
    this.raycaster = await t.getModule(RaycasterModule)
    for (let e = 0; e < Fatcaster.rayCount; ++e) this.rayAssembly[e] = new THREE.Ray()
    if (e.debug) {
      const e = (await t.getModuleBySymbol<WebGLRendererModule>(SymbolList.WEBGL_RENDERER)).getScene()
      for (let t = 0; t < Fatcaster.rayCount; ++t) {
        this.rayVisuals[t] = new THREE.ArrowHelper(new THREE.Vector3(), new THREE.Vector3())
        const i = this.rayVisuals[t]
        i.remove(i.cone), i.setLength(1e3), e.add(this.rayVisuals[t])
      }
    }
  }
  async dispose(e) {
    const t = (await e.getModuleBySymbol(SymbolList.WEBGL_RENDERER)).getScene()
    for (const e of this.rayVisuals) t.remove(e)
  }
  get ray() {
    return this.raycaster.pointer.pointerRay.clone()
  }
  cast(e, t, i: Function = Filter.AVERAGE) {
    const n = this.ray
    this.updateRayAssembly(n.origin, n.direction, e)
    return this.castRays(t, i)
  }
  castRays(e, t: Function) {
    const i = []
    let n = null
    for (const t of this.rayAssembly) {
      const s = this.raycaster.picking.pick(t.origin, t.direction, e)
      s && (t === this.rayAssembly[0] && (n = s), s.point.add(this.rayAssembly[0].origin).sub(t.origin), i.push(s))
    }
    return t(i, n, this.rayAssembly)
  }
  updateRayAssembly(e, t, i) {
    for (const e of this.rayAssembly) e.direction.copy(t)
    this.rayPlaneOrientation.setFromUnitVectors(Vectors.FORWARD, t)
    this.rayAssembly[0].origin.copy(e)
    this.rayAssembly[1].origin.copy(Vectors.RIGHT).multiplyScalar(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[2].origin.copy(Vectors.UP).multiplyScalar(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[3].origin.copy(Vectors.LEFT).multiplyScalar(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[4].origin.copy(Vectors.DOWN).multiplyScalar(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[5].origin.copy(Vectors.UP).add(Vectors.RIGHT).setLength(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[6].origin.copy(Vectors.UP).add(Vectors.LEFT).setLength(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[7].origin.copy(Vectors.DOWN).add(Vectors.RIGHT).setLength(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    this.rayAssembly[8].origin.copy(Vectors.DOWN).add(Vectors.LEFT).setLength(i).applyQuaternion(this.rayPlaneOrientation).add(e)
    if (this.config.debug)
      for (let e = 0; e < this.rayVisuals.length; ++e) {
        const i = this.rayVisuals[e]
        i.setDirection(t)
        i.position.copy(this.rayAssembly[e].origin)
      }
  }
}
Fatcaster.rayCount = 9
